package com.rfid.api;

import com.rfid.def.MIFARE_CLASSIC;
import com.rfid.reader.PCReader;
import com.rfid.spec.ISO14443ATagDataReport;
import com.rfid.spec.L0_PICC_HANDLE;
import com.rfid.spec.SpecAIPInvenParamISO14443A;
import com.rfid.spec.SpecConnPrmsISO114443a;
import com.rfid.spec.SpecConnPrmsMfclS50;
import com.rfid.spec.SpecConnPrmsMfclUltralight;
import com.rfid.spec.SpecInvenParamSet;
import com.rfid.spec.SpecTagAccessSet;
import com.rfid.spec.SpecTagTranscISO14443a;
import com.rfid.spec.TagOperateHandle;
import com.rfid.spec.tagaccess.MFCL_SpecAuthenticate;
import com.rfid.spec.tagaccess.MFCL_SpecDecrement;
import com.rfid.spec.tagaccess.MFCL_SpecFormatValueBlock;
import com.rfid.spec.tagaccess.MFCL_SpecIncrement;
import com.rfid.spec.tagaccess.MFCL_SpecReadBlock;
import com.rfid.spec.tagaccess.MFCL_SpecRestore;
import com.rfid.spec.tagaccess.MFCL_SpecWriteBlock;
import com.rfid.spec.tagaccess.Ultralight_SpecReadMultiplePages;
import com.rfid.spec.tagaccess.Ultralight_SpecWriteMultiplePages;
import com.rfid.transport.NdkSupport;

public class ISO14443AInterface {
    private TagOperateHandle hTag = new TagOperateHandle();
    private PCReader m_reader = null;

    public static int ISO14443A_ParseTagDataReport(Object hTagReport, ISO14443ATag tagData) {
        ISO14443ATagDataReport ptdr = (ISO14443ATagDataReport) hTagReport;
        if (ptdr == null) return -3;
        if (ptdr.m_AIPType != 2L) return -4;
        tagData.uid = new byte[ptdr.m_uidlen];
        tagData.aip_id = ptdr.m_AIPType;
        tagData.tag_id = ptdr.m_PICCType;
        tagData.ant_id = ptdr.m_AntID;
        System.arraycopy(ptdr.m_uid, 0, tagData.uid, 0, ptdr.m_uidlen);
        return 0;
    }

    public static String GetTagNameById(long picc_tid) {
        if (picc_tid == 1L) return "Mifare Ultralight";
        if (picc_tid == 2L) return "Mifare S50";
        if (picc_tid == 3L) return "Mifare S70";
        return "Unknown Tag";
    }

    public static SpecAIPInvenParamISO14443A ISO14443A_CreateInvenParam(Object hInvenParamSpecList, byte AntennaID) {
        SpecInvenParamSet pInvenParam = (SpecInvenParamSet) hInvenParamSpecList;
        if (pInvenParam == null) return null;
        SpecAIPInvenParamISO14443A iso14443aprm = SpecAIPInvenParamISO14443A.create();
        if (iso14443aprm == null) return null;
        iso14443aprm.m_aipType = 2L;
        iso14443aprm.m_antID = AntennaID;
        pInvenParam.AddAirProtocol(iso14443aprm);
        return iso14443aprm;
    }

    private static SpecConnPrmsMfclS50 MFCL_CreateConnectParam(byte tagType, byte[] uid) {
        SpecConnPrmsMfclS50 p = SpecConnPrmsMfclS50.Create();
        if (p != null) {
            if (tagType == 0) p.m_TagId = 2L;
            else p.m_TagId = 3L;
            System.arraycopy(uid, 0, p.m_UID, 0, MIFARE_CLASSIC.UID_LEN);
            p.m_UIDLen = (byte) MIFARE_CLASSIC.UID_LEN;
        }
        return p;
    }

    public int MFCL_Connect(PCReaderInterface hr, byte tagType, byte[] uid) {
        int iret = -1;
        SpecConnPrmsMfclS50 conn_params = MFCL_CreateConnectParam(tagType, uid);
        if (conn_params == null) return -12;
        this.m_reader = hr.GetReader();
        iret = this.m_reader.PICC_Connect(conn_params, this.hTag);
        if (iret != 0) this.m_reader = null;
        return iret;
    }

    public int ISO14443A_Disconnect() {
        if (this.m_reader == null) return 0;
        int iret = this.m_reader.PICC_DisConnect(this.hTag.getTagHandle());
        if (iret == 0) this.hTag.setTagHandle(null);
        return iret;
    }

    private static MFCL_SpecAuthenticate MFCL_CreateTAAuthenticate(SpecTagAccessSet hTagAccessSet, byte blkAddr, byte keyType, byte[] key) {
        MFCL_SpecAuthenticate p = MFCL_SpecAuthenticate.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            p.m_reqKeyType = keyType;
            if (key != null) p.m_reqKey.WriteData(key, 0, MIFARE_CLASSIC.KEY_LEN);
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_Authenticate(byte blkAddr, byte keyType, byte[] key) {
        if (this.m_reader == null) return -1;
        MFCL_SpecAuthenticate tagAccess = MFCL_CreateTAAuthenticate(null, blkAddr, keyType, key);
        if (tagAccess == null) return -12;
        return this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
    }

    private static MFCL_SpecReadBlock MFCL_CreateTAReadBlock(SpecTagAccessSet hTagAccessSet, byte blkAddr) {
        MFCL_SpecReadBlock p = MFCL_SpecReadBlock.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_ReadBlock(byte blkAddr, byte[] blkData) {
        if (this.m_reader == null) return -1;
        MFCL_SpecReadBlock tagAccess = MFCL_CreateTAReadBlock(null, blkAddr);
        if (tagAccess == null || blkData == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return MFCL_ParseReadBlockResult(tagAccess, blkData);
    }

    private static int MFCL_ParseReadBlockResult(MFCL_SpecReadBlock tagAccess, byte[] blkData) {
        int nSize = blkData.length;
        if (nSize > 16) nSize = 16;
        MFCL_SpecReadBlock p = tagAccess;
        if (p == null) return -12;
        p.m_respBlkData.ReadData(blkData, nSize);
        return 0;
    }

    private static MFCL_SpecWriteBlock MFCL_CreateTAWriteBlock(SpecTagAccessSet hTagAccessSet, byte blkAddr, byte[] blkData) {
        MFCL_SpecWriteBlock p = MFCL_SpecWriteBlock.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            p.m_reqBlkData.WriteData(blkData, 0, 16);
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_WriteBlock(byte blkAddr, byte[] blkData) {
        if (this.m_reader == null) return -1;
        MFCL_SpecWriteBlock tagAccess = MFCL_CreateTAWriteBlock(null, blkAddr, blkData);
        if (tagAccess == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private static MFCL_SpecFormatValueBlock MFCL_CreateTAFormatValueBlock(SpecTagAccessSet hTagAccessSet, byte blkAddr, long initValue) {
        MFCL_SpecFormatValueBlock p = MFCL_SpecFormatValueBlock.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            p.m_reqInitValue = initValue;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_FormatValueBlock(byte blkAddr, long initValue) {
        if (this.m_reader == null) return -1;
        MFCL_SpecFormatValueBlock tagAccess = MFCL_CreateTAFormatValueBlock(null, blkAddr, initValue);
        if (tagAccess == null) return 0;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private static MFCL_SpecRestore MFCL_CreateTARestore(SpecTagAccessSet hTagAccessSet, byte blkAddr) {
        MFCL_SpecRestore p = MFCL_SpecRestore.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_Restore(byte blkAddr) {
        if (this.m_reader == null) return -1;
        MFCL_SpecRestore tagAccess = MFCL_CreateTARestore(null, blkAddr);
        if (tagAccess == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private static MFCL_SpecIncrement MFCL_CreateTAIncrement(SpecTagAccessSet hTagAccessSet, byte blkAddr, long val) {
        MFCL_SpecIncrement p = MFCL_SpecIncrement.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            p.m_reqVal = val;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_Increment(byte blkAddr, long val) {
        if (this.m_reader == null) return -1;
        MFCL_SpecIncrement tagAccess = MFCL_CreateTAIncrement(null, blkAddr, val);
        if (tagAccess == null) return 0;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private static MFCL_SpecDecrement MFCL_CreateTADecrement(SpecTagAccessSet hTagAccessSet, byte blkAddr, long val) {
        MFCL_SpecDecrement p = MFCL_SpecDecrement.Create();
        if (p != null) {
            p.m_reqBlkAddr = blkAddr;
            p.m_reqVal = val;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    public int MFCL_Decrement(byte blkAddr, long val) {
        if (this.m_reader == null) return -1;
        MFCL_SpecDecrement tagAccess = MFCL_CreateTADecrement(null, blkAddr, val);
        if (tagAccess == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private static SpecConnPrmsMfclUltralight ULTRALIGHT_CreateConnectParam(byte[] uid) {
        SpecConnPrmsMfclUltralight pConnParams = SpecConnPrmsMfclUltralight.Create();
        if (pConnParams != null) {
            System.arraycopy(uid, 0, pConnParams.m_UID, 0, 7);
            pConnParams.m_UIDLen = 7;
        }
        return pConnParams;
    }

    public int ULTRALIGHT_Connect(PCReaderInterface hr, byte[] uid) {
        int iret = -1;
        SpecConnPrmsMfclUltralight conn_params = ULTRALIGHT_CreateConnectParam(uid);
        if (conn_params == null) return -12;
        this.m_reader = hr.GetReader();
        iret = this.m_reader.PICC_Connect(conn_params, this.hTag);
        if (iret != 0) this.m_reader = null;
        return iret;
    }

    private static Ultralight_SpecReadMultiplePages ULTRALIGHT_CreateTAReadMultiplePages(SpecTagAccessSet hTagAccessSet, long pageStart, long pageNum) {
        Ultralight_SpecReadMultiplePages p = Ultralight_SpecReadMultiplePages.Create();
        if (p != null) {
            p.m_reqPageStart = pageStart;
            p.m_reqPageNum = pageNum;
            SpecTagAccessSet pSet = hTagAccessSet;
            if (pSet != null) pSet.AddTagAccess(p);
        }
        return p;
    }

    private static int ULTRALIGHT_ParseReadMultiplePagesResult(Ultralight_SpecReadMultiplePages tagAccess, byte[] databuf, Integer nSize) {
        Ultralight_SpecReadMultiplePages p = tagAccess;
        if (p == null) return -3;
        int mRead = p.m_respPageData.ReadData(databuf, databuf.length);
        NdkSupport.SetIntegerValue(nSize, mRead);
        return 0;
    }

    public int ULTRALIGHT_ReadMultiplePages(int pageStart, int pageNum, byte[] databuf, Integer nSize) {
        Ultralight_SpecReadMultiplePages tagAccess = ULTRALIGHT_CreateTAReadMultiplePages(null, pageStart, pageNum);
        if (tagAccess == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return ULTRALIGHT_ParseReadMultiplePagesResult(tagAccess, databuf, nSize);
    }

    private static Ultralight_SpecWriteMultiplePages ULTRALIGHT_CreateTAWriteMultiplePages(SpecTagAccessSet hTagAccessSet, long pageStart, long pageNum, byte[] databuf, int bytesToWrite) {
        Ultralight_SpecWriteMultiplePages p = Ultralight_SpecWriteMultiplePages.Create();
        if (p != null) {
            p.m_reqPageStart = pageStart;
            p.m_reqPageNum = pageNum;
            p.m_reqPageDataToWrite.WriteData(databuf, 0, bytesToWrite);
        }
        return p;
    }

    public int ULTRALIGHT_WriteMultiplePages(int pageStart, int pageNum, byte[] databuf, int bytesToWrite) {
        if (databuf == null || databuf.length < bytesToWrite) return -3;
        Ultralight_SpecWriteMultiplePages tagAccess = ULTRALIGHT_CreateTAWriteMultiplePages(null, pageStart, pageNum, databuf, bytesToWrite);
        if (tagAccess == null) return -12;
        int iret = this.m_reader.PICC_Access(this.hTag.getTagHandle(), tagAccess);
        if (iret != 0) return iret;
        return iret;
    }

    private SpecConnPrmsISO114443a ISO14443A_CreateConnectParam(int tagtype, byte[] uid, byte uidLen) {
        SpecConnPrmsISO114443a pConnParams = SpecConnPrmsISO114443a.Create();
        if (pConnParams != null) {
            if (uidLen > pConnParams.m_UID.length) uidLen = (byte) pConnParams.m_UID.length;
            System.arraycopy(uid, 0, pConnParams.m_UID, 0, uidLen);
            pConnParams.m_UIDLen = uidLen;
            pConnParams.m_TagId = tagtype;
        }
        return pConnParams;
    }

    public int ISO14443A_Connect(PCReaderInterface hr, int tagType, byte[] uid, byte uidLen) {
        SpecConnPrmsISO114443a conn_params = ISO14443A_CreateConnectParam(tagType, uid, uidLen);
        this.m_reader = hr.GetReader();
        int iret = this.m_reader.PICC_Connect(conn_params, this.hTag);
        if (iret != 0) this.m_reader = null;
        return iret;
    }

    public int ISO14443Ap4_Transceive(byte[] sndData, int sndLen, byte[] rcvBuff, int[] nSize) {
        int iret = -1;
        L0_PICC_HANDLE hL0PICC = this.hTag.getTagHandle();
        if (hL0PICC == null) return -3;
        SpecTagTranscISO14443a p = SpecTagTranscISO14443a.Create();
        if (p == null) return -1;
        p.m_EnableP4 = true;
        p.m_reqData.WriteData(sndData, 0, sndLen);
        iret = this.m_reader.PICC_Transceive(hL0PICC, p);
        if (iret == 0) {
            int nCopy = p.m_respData.readable_length();
            if (nCopy > nSize[0]) nCopy = nSize[0];
            p.m_respData.ReadData(rcvBuff, nCopy);
            nSize[0] = nCopy;
        }
        return iret;
    }
}
